//------------------------------------------------------------------------------
//#define DEBUG_VARPCF

//------------------------------------------------------------------------------
// default global variables

const float4x4 g_WorldMtx 				: WORLD_MTX;
const float4x4 g_ViewMtx				: VIEW_MTX;
const float4x4 g_ProjMtx				: PROJ_MTX;
const float4x4 g_WorldViewProjMtx		: WORLDVIEWPROJ_MTX;

// app specific global variables

const float4 g_Ambient					: AMBIENT_COLOR;
const float3 g_CameraPos				: CAMERA_POS;
const float3 g_LightPos					: LIGHT_POS;
const float4 g_LightPlane				: LIGHT_PLANE;
const float4 g_LightViewMtx				: LIGHTVIEW_MTX;
const float4x4 g_LightViewProjMtx		: LIGHTVIEWPROJ_MTX;
const float4x4 g_LightViewProjTexMtx	: LIGHTVIEWPROJTEX_MTX;
const float g_ShadowMapSize				: SHADOWMAP_SIZE;
const float2 g_InvShadowMapSize			: INV_SHADOWMAP_SIZE;
const float g_ShadowMapBias				: SHADOWMAP_BIAS;

const float g_VarPCFDistFactor : VAR_PCF_DISTANCE_FACTOR = 1.0;

const float g_ParallaxScale : PARALLAX_SCALE = 0.02;

//------------------------------------------------------------------------------
struct a2vApplyBasic {
	float4 pos		: POSITION;
	float3 normal	: NORMAL;
	float2 uv		: TEXCOORD0;
	float3 tangent	: TANGENT;
	float3 binormal	: BINORMAL;
};

struct v2fApplyBasic {
	float4 pos		: POSITION;
	float2 uv		: TEXCOORD0;
	float3 viewDir	: TEXCOORD1;
	float3 lightDir	: TEXCOORD2;
	float4 lsPos	: TEXCOORD3;
	float3 parallax	: TEXCOORD4;
};

struct a2vGenerateBasic {
	float4 pos		: POSITION;
	float2 uv		: TEXCOORD0;
};

struct v2fGenerateBasic {
	float4 pos		: POSITION;
	float2 uv		: TEXCOORD0;
	float4 lsPos	: TEXCOORD1;
};

struct f2fb {
	float4 c0 		: COLOR0;
};

//------------------------------------------------------------------------------
sampler BaseMapSamp : register(s0);
sampler NormalMapSamp : register(s1);

//------------------------------------------------------------------------------
// Common Operations

//--------------------------------------
void PerspectiveXform(
	out float4 outCoord,
	float4x4 xForm,
	in float4 inCoord) {
	
	// linear-depth prespective xform
	outCoord = mul(xForm, inCoord);
	outCoord.z *= outCoord.w;
}

//--------------------------------------
void LightSpaceDepth(
	out float outDepth,
	in float4 inWsCoord) {
	
	outDepth = dot(inWsCoord, g_LightPlane);
}

//--------------------------------------
void ShadowMapBaseTexcoord(
	out float2 outCoord,
	in float2 inCoord) {
	
	outCoord = inCoord + g_InvShadowMapSize.y;
}

//--------------------------------------
void ShadowMapBaseTexcoordEx(
	out float2 outCoord,
	in float2 inCoord,
	in float inInvHalfShadowMapSize) {
	
	outCoord = inCoord + inInvHalfShadowMapSize;
}

//--------------------------------------
void FetchOffsetedSample(
	out float outSample,
	in sampler inShadowMapSamp,
	in float2 inTexcoord,
	in float2 inOffset) {
	
	outSample = tex2D(inShadowMapSamp, inTexcoord + inOffset * g_InvShadowMapSize.x).x;
}

//--------------------------------------
void FetchOffsetedSampleEx(
	out float4 outSample,
	in sampler inShadowMapSamp,
	in float2 inTexcoord,
	in float2 inOffset,
	in float inInvShadowMapSize) {
	
	float4 uv = float4(inTexcoord + inOffset * inInvShadowMapSize, 0, 0);
	
	outSample = tex2Dlod(inShadowMapSamp, uv);
}

//--------------------------------------
void BilinearFilterSample(
	out float outSample,
	in float inS00,
	in float inS01,
	in float inS10,
	in float inS11,
	in float2 inWeight) {

	outSample = lerp(
		lerp(inS00, inS01, inWeight.x),
		lerp(inS10, inS11, inWeight.x),
		inWeight.y);
}

//--------------------------------------
void BilinearInterpolateResults3x3(
	out float outShadowTerm,
	in float3 inResults[3],
	in float2 inWeight) {
	
	outShadowTerm = 0;
	
	float4 s00, s01, s10, s11;
	
	s00[0] = inResults[0][0];
	s01[0] = inResults[0][1];
	s10[0] = inResults[1][0];
	s11[0] = inResults[1][1];
	
	s00[1] = inResults[0][1];
	s01[1] = inResults[0][2];
	s10[1] = inResults[1][1];
	s11[1] = inResults[1][2];
	
	s00[2] = inResults[1][0];
	s01[2] = inResults[1][1];
	s10[2] = inResults[2][0];
	s11[2] = inResults[2][1];
	
	s00[3] = inResults[1][1];
	s01[3] = inResults[1][2];
	s10[3] = inResults[2][1];
	s11[3] = inResults[2][2];
	
	float4 temp = lerp(
			lerp(s00, s01, inWeight.x),
			lerp(s10, s11, inWeight.x),
			inWeight.y);
			
	outShadowTerm = dot(temp, 0.25);
	
}

//--------------------------------------
void BilinearInterpolateResults4x4(
	out float outShadowTerm,
	in float4 inResults[4],
	in float2 inWeight) {
	
	outShadowTerm = 0;

	for(int i=0; i<3; ++i) {
		float3 s00, s01, s10, s11;
		
		for(int j=0; j<3; ++j) {
			s00[j] = inResults[i][j];
			s01[j] = inResults[i][j+1];
			s10[j] = inResults[i+1][j];
			s11[j] = inResults[i+1][j+1];
		}
		
		float3 temp = lerp(
			lerp(s00, s01, inWeight.x),
			lerp(s10, s11, inWeight.x),
			inWeight.y);
		
		outShadowTerm += dot(temp, 1.0 / 9.0);
	}
	
}

//--------------------------------------
void BilinearInterpolateResults8x8(
	out float outShadowTerm,
	in float4 inResults[16],
	in float2 inWeight) {
	
	outShadowTerm = 0;

	for(int i=0; i<15; i += 2) {
	
		float3 s00, s01, s10, s11;
		float4 s02, s03, s12, s13;
		
		for(int j=0; j<3; ++j) {
			
			s00[j] = inResults[i  ][j  ];
			s01[j] = inResults[i  ][j+1];
			s10[j] = inResults[i+2][j  ];
			s11[j] = inResults[i+2][j+1];
			
			s02[j] = inResults[i+1][j  ];
			s03[j] = inResults[i+1][j+1];
			s12[j] = inResults[i+3][j  ];
			s13[j] = inResults[i+3][j+1];
		}
		
		s02[3] = inResults[i][3];
		s03[3] = inResults[i+1][0];
		s12[3] = inResults[i+2][3];
		s13[3] = inResults[i+3][0];
		
		float3 temp3 = lerp(
			lerp(s00, s01, inWeight.x),
			lerp(s10, s11, inWeight.x),
			inWeight.y);
		
		float4 temp4 = lerp(
			lerp(s02, s03, inWeight.x),
			lerp(s12, s13, inWeight.x),
			inWeight.y);
		
		outShadowTerm += dot(temp3, 1.0 / 49.0);
		outShadowTerm += dot(temp4, 1.0 / 49.0);
	}
	
}


//--------------------------------------
void ApplyBasicVertexProcess(
	out v2fApplyBasic output,
	in a2vApplyBasic input) {
	
	float4 worldPos = mul(g_WorldMtx, input.pos);
	
	PerspectiveXform(output.pos, g_WorldViewProjMtx, input.pos);
	
	PerspectiveXform(output.lsPos, g_LightViewProjTexMtx, worldPos);
	
	LightSpaceDepth(output.lsPos.z, worldPos);
	
	float3 n = normalize( mul(g_WorldMtx, input.normal) );
	float3 t = normalize( mul(g_WorldMtx, input.tangent) );
	float3 b = cross(n, -t);
	
	float3x3 mtxTS = float3x3(t, b, n);
	
	output.lightDir	= mul(mtxTS, g_LightPos - worldPos);
	output.viewDir	= mul(mtxTS, g_CameraPos - worldPos);
	output.parallax = normalize(output.viewDir);
	
	output.parallax *= float3(1,-1, 1);
	
	output.uv		= input.uv;
}

//--------------------------------------
void ApplyBasicFragmentProcess(
	out float4 output,
	in v2fApplyBasic input,
	in float3 shadowTerm) {
	
	#if 0
	float displacement = tex2D(NormalMapSamp, input.uv).w;
	displacement = displacement * g_ParallaxScale + (g_ParallaxScale * -0.5);
	input.uv -= (input.parallax.xy / input.parallax.z) * displacement;
	#endif
	
	float3 l = normalize( input.lightDir );
	float3 n = normalize( tex2D(NormalMapSamp, input.uv).xyz * 2 - 1 );
	float3 r = normalize( reflect(-input.viewDir, n) );
	
	float diff = saturate( dot(n, l) );
	float spec = pow(saturate( dot(r, l) ), 8.0);
	
	float4 baseMap = tex2D(BaseMapSamp, input.uv);
	
	output.xyz	= baseMap * ( ( diff + spec * baseMap.w ) * shadowTerm + g_Ambient );
	output.w	= baseMap.w;
}

//--------------------------------------
void GenerateBasicVertexProcess(
	out v2fGenerateBasic output,
	in a2vGenerateBasic input) {
	
	float4 worldPos = mul(g_WorldMtx, input.pos);
	
	PerspectiveXform(output.pos, g_LightViewProjMtx, worldPos);
	
	output.lsPos = output.pos;
	LightSpaceDepth(output.lsPos.z, worldPos);
	
	output.uv		= input.uv;
}

//--------------------------------------
void GenerateBasicFragmentProcess(
	out float4 output,
	in v2fGenerateBasic input) {
	
	output	= input.lsPos.z + g_ShadowMapBias;
}

//------------------------------------------------------------------------------
